<?php
/**
 * Class handling all the loading of files, can handle loading of libraries based on directory dependency
 * @package utilities
 * @author ayo
 * @since Oct 9, 2011
 * @version 1.0
 * @see 
 */
class FileLoader 
{
	/**
	 * 
	 * instance of the file loader, singleton creational design pattern
	 * @var Object
	 * @access private static
	 */
	private static $o_instance;
	/**
	 * 
	 * array structure containing the different constants
	 * @var Array
	 * @access protected
	 */
	public $arr_constants;
	
	/**
	 * 
	 * current library being loaded
	 * @var string
	 * @access protected 
	 */
	protected $s_currLibrary;
	
	/**
	 * 
	 * libraries that have been already loaded
	 * @var Array
	 * @access protected
	 */
	protected $arr_loadedLibraries;
	
	/**
	 * 
	 * directories that have been loaded
	 * @var Array
	 * @access protected
	 */
	protected $arr_loadedDirs;
	
	/**
	 * 
	 * files that have been loaded
	 * @var Array
	 * @access
	 */
	protected $arr_loadedFiles;
	
	/**
	 * Constructor 
	 * initilizes the neccessary variables in the class when creating it
	 */
	private function FileLoader()
	{
		//say no to hard coded string literals
		//why define the name of the directories? security purpose for later changing and obfuscation
		$this->arr_constants = array(
			'abstracts'=> '/abstracts',
			'interfaces'=> '/interfaces',
			'loadedFiles'=> 'loadedFiles',
			'extension'=> 'php',
			'slash'=> '/',
			'dot'=> '.',
			'libPath'=> 'libPath',
			'fileList'=> 'fileList'
		);
		
		//for loading libraries
		$this->s_currLibrary = null;
		$this->arr_loadedLibraries = array();
		//for loading files individually
		$this->arr_loadedFiles = array();
		//for loading directories
		$this->arr_loadedDirs = array();
	}
	
	public static function getInstance()
	{
		if(!isset(self::$o_instance) ||  self::$o_instance == null)
		{
			self::$o_instance = new FileLoader();
		}
		return self::$o_instance;
	}
	
	/**
	 * Handles the loading of a library, loads it based on the dependencies<br>
	 * abstracts first then interfaces
	 *  
	 * @param String $s_libName
	 * @param String_type $s_dirPath
	 * @return boolean
	 */
	public function loadLibrary($s_libName, $s_dirPath)
	{
		//set the current library name that is being loaded
		$this->s_currLibrary = $s_libName;
		//check if the library is loaded already
		//check if the index of the array is set
		if(!isset($this->arr_loadedLibraries[$s_libName]))
		{
			//keep reference of files loaded
			//$this->arr_loadedLibraries[$s_libName] = $s_dirPath;
			$this->arr_loadedLibraries[$s_libName] = array(
				$this->arr_constants['libPath']=> $s_dirPath,
				$this->arr_constants['fileList']=> array()
			);
			//make sure the dependencies are loaded first before the main class files
			//abstracts first
			if(is_dir($s_dirPath.$this->arr_constants['abstracts']))
			{
				$this->loadFiles($s_dirPath.$this->arr_constants['abstracts']);
			}
			//interfaces next
			if(is_dir($s_dirPath.$this->arr_constants['interfaces']))
			{
				$this->loadFiles($s_dirPath.$this->arr_constants['interfaces']);
			}
			//load the main class files
			$this->loadFiles($s_dirPath);
			//return to null after all the processing is done
			$this->s_currLibrary = null;
		}
		else
		{
			//return to null after all the processing is done
			$this->s_currLibrary = null;
			//return false if an error happens
			return false;
		}
	}
	
	/**
	 * 
	 * Handles loading files under a directory name
	 * @param String $s_dirPath
	 */
	public function loadFiles($s_dirPath)
	{
		//check if it is a directory
		if(is_dir($s_dirPath))
		{
			//check if the directory has been loaded already
			if(!in_array($s_dirPath, $this->arr_loadedDirs))
			{
				//open the directory
				$dir = opendir($s_dirPath);
				//read the directory contents
				while(false !== ($s_path = readdir($dir)))
				{
					//removes the files that where read that have the .. . as the file name.
					if ($s_path != $this->arr_constants['dot'] && $s_path != $this->arr_constants['dot'].$this->arr_constants['dot']) 
					{
						//concatenate the basepath of the directory with the / and then path to the file
						$s_fileName = $s_dirPath.$this->arr_constants['slash'].$s_path;
						//get the file extension
						$s_ext = pathinfo($s_fileName, PATHINFO_EXTENSION);
						//check if it is a dir 
						if(file_exists($s_fileName) && !is_dir($s_fileName))
						{
							//load only php files
							if($s_ext == $this->arr_constants['extension'])
							{
								$this->loadFile($s_fileName);
							}
						}
						else
						{
							//is a dir so load the contents
							$this->loadFiles($s_fileName);
						}
					}
				}
				//save in the class attribute the loaded directories
				array_push($this->arr_loadedDirs, $s_dirPath);
				//close the opened directory
				closedir($dir);
			}
		}
	}
	
	/**
	 * 
	 * loads the file that has been given to it then push it into the class attribute loaded files and loaded libraries file list if needed
	 * @param String $s_filePath
	 */
	public function loadFile($s_filePath)
	{
		//check if it is not a dir
		if(!is_dir($s_filePath))
		{
			//check if the file exists
			if(file_exists($s_filePath))
			{
				if(!in_array($s_filePath, $this->arr_loadedFiles))
				{
					//require the needed files
					require_once $s_filePath;
					//echo $s_filePath.'<br>';
					//saves filepath directly to class attributte or saves the filepaths to the loaded files array
					if($this->s_currLibrary != null)
					{
						if(isset($this->arr_loadedLibraries[$this->s_currLibrary]))
						{
							//save the file list of the loaded items in it for later purposes and logging
							array_push($this->arr_loadedLibraries[$this->s_currLibrary][$this->arr_constants['fileList']], $s_filePath);
						}
					}
					//push into the loaded files array class attributte
					array_push($this->arr_loadedFiles, $s_filePath);
				}
			}
		}
	}
	
	public function includeFile($s_filePath)
	{
		if(file_exists($s_filePath))
		{
			include_once $s_filePath;
			array_push($this->arr_loadedFiles, $s_filePath);
		}
	}
	
	public function __destruct()
	{
		
	}
}
?>